<?php
/*
 * Copyright (c) 2008 Sergey Bondari, bondari@bondari.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

namespace Mosaic\System;

/**
 * Response entity
 *
 * Response to the request - encapsulates serialized responce data or remote exception.
 *
 * @package System
 * @subpackage Requests
 */
class Response implements ifRequestEntity {
	/**
	 * Request token
	 * @var string
	 */
	protected $__RID;

	/**
	 * Response data
	 * @var mixed
	 */
	protected $__DATA;


	/**
	 * Context tag of request
	 * @var string
	 */
	protected $__CONTEXT;


	/**
	 * Creates MCGI packet.
	 *
	 * Encapsulates response into the MCGI packet object instance.
	 *
	 * @param integer Protocol version
	 * @return \Mosaic\MCGI\Packet instance
	 */
	public function createMCGIPacket($protocol = \Mosaic\MCGI\MCGI) {
		$mcgi = new \Mosaic\MCGI\Packet($protocol);
		if ($protocol == \Mosaic\MCGI\MCGI) {
			$mcgi->setHeader('REQUEST_TOKEN', $this->getRequestToken());
			$mcgi->setHeader('RESPONSE', '1');
			$mcgi->setBody(serialize($this));
		} else {
			$data = $this->getData();
			if (is_string($data)) {
				$mcgi->setBody($data);
			} else if (is_array($data)) {
				$mcgi->setBody($data['body']);
				if (is_array($data['headers'])) foreach ($data['headers'] as $k => $v) $mcgi->setHeader($k, $v);
			} else {
				$mcgi->setBody(serialize($data));
			}
		}
		return $mcgi;
	}


	/**
	 * Creates a response instance.
	 *
	 * Creates a Response instance from a MCGI packet, that contains serialized Response
	 *
	 * @param \Mosaic\MCGI\Packet Packet instance
	 * @return Response 
	 */
	public static function createFromMCGIPacket(\Mosaic\MCGI\Packet $mcgi) {
		$res = unserialize($mcgi->getBody());
		if (!$res instanceof Response) {
			throw new RuntimeException('Invalid MCGI body content. \Mosaic\System\Response expected.');
		}

		return $res;
	}

	/**
	 * Response constructor.
	 * 
	 * @param string Request instance to which response is related
	 */
	function __construct(Request $req) {
		$this->__RID = $req->getRequestToken();
	}

	/**
	 * Returns request token of the response
	 * 
	 * @return string Request token
	 */
	public function getRequestToken() {
		return $this->__RID;
	}


	/**
	 * Returns response data. 
	 *
	 * Returns data encapsulated into response. If data is an exception, throws a exception.
	 * Thus this function must be called *ONLY* before returning the response value to the request caller.
	 * 
	 * @return mixed Response data
	 */
	public function getData() {
		if (@$this->__SERIALIZED) {
			$this->__DATA = unserialize($this->__DATA);
			$this->__SERIALIZED = false;
		}

		if ($this->__DATA instanceof RemoteExceptionContainer) {
			trigger_error('Response is the remote exception. Throwing exception ...');
			$this->__DATA->raise();
		}
		return $this->__DATA;
	}

	/**
	 * Fill response with data.
	 *
	 * If parameter is an exception class, encapsulates exception into the exception container.
	 * Note that only RemoteExceptionThrowable exceptions are tranferred correctly between 
	 * the distributed environment elements.
	 * 
	 * @param mixed Response data
	 * @return void
	 */
	public function setData($data) {
		if ($data instanceof RemoteExceptionThrowable) $data = new RemoteExceptionContainer($data);

		if (is_object($data)) {
			$this->__DATA = serialize($data);
			$this->__SERIALIZED = true;
		} else $this->__DATA = $data;
	}




	/**
	 * Sends response.
	 *
	 * Send response through the socket via MCGI protocol
	 *
	 * @param resource Opened stream resource
	 * @deprecated This function is deprecated and will be soon removed
	 */
	public function send($socket) {
		$mcgi = $this->createMCGIPacket();
		$mcgi->send($socket);
	}


	/**
	 * Receive response.
	 *
	 * This function performs a receive operation on a socket. 
	 
	 * MCGI protocol.
	 * @deprecated This function is deprecated and will be soon removed
	 * @param resource Opened stream resource
	 * @return Response
	 */
	public static function receive($socket) {
		trigger_error("Receiving respone ...");
		$mcgi = new \Mosaic\MCGI\Packet();
		$mcgi->receive($socket);
		trigger_error("Request received.");
		switch ($mcgi->protocol()) {
			case \Mosaic\MCGI\SCGI:
				throw new IOException('SCGI requests are not supported');
			break;
	                case \Mosaic\MCGI\MCGI;
				return Response::createFromMCGIPacket($mcgi);
			break;
		}
	}


	/**
	 * Context tag setter 
	 * @param string Context tag
	 */
	public function setContextTag($v) {
		$this->__CONTEXT = $v;
	}

	/**
	 * Context tag getter
	 * @return string Context tag
	 */
	public function getContextTag() {
		return $this->__CONTEXT;
	}



}
